package dubbot

import (
	"context"
	"dubbo.apache.org/dubbo-go/v3/common"
	dubboconst "dubbo.apache.org/dubbo-go/v3/common/constant"
	dubboc "dubbo.apache.org/dubbo-go/v3/config"
	"dubbo.apache.org/dubbo-go/v3/config/generic"
	hessian "github.com/apache/dubbo-go-hessian2"
	"github.com/hyperway/pkg/component"
	"github.com/hyperway/pkg/logger"
	"sync"
)

type (
	DubboInvoker struct {
		lock *sync.Mutex
	}
)

// Invoke 调用dubbo服务，反序列化dubbo结果
func (d *DubboInvoker) Invoke(ctx *DubboContext) (*component.ServerResponse, *component.ServerError) {

	trace := ctx.trace
	ret, e := d.invoke(ctx)

	if e != nil {
		trace.Errorw("[dubbo转发失败]", "error", e)
		return nil, &component.ServerError{
			StatusCode: 502,
			Message:    "dubbo转发失败",
			ErrorCode:  "DUBBO_INVOKE_ERROR",
		}
	}

	select {
	case <-ctx.Context.Done():
		return nil, &component.ServerError{
			StatusCode: 500,
			Message:    "dubbo请求取消",
			ErrorCode:  "DUBBO_REQUEST_CANCEL",
			Cause:      ctx.Context.Err(),
		}
	default:
		break
	}

	result, err := decoder(ret)
	if err != nil {
		trace.Errorw("[dubbo结果解析失败]", "error", err)
		return nil, &component.ServerError{
			StatusCode: 500,
			Message:    "dubbo结果解析失败",
			ErrorCode:  "DUBBO_RESULT_PARSE_ERROR",
		}
	}

	return result, nil
}

// invoke 调用dubbo服务
func (d *DubboInvoker) invoke(ctx *DubboContext) (interface{}, *component.ServerError) {

	trace := logger.Trace(ctx.Context)

	proxy := d.loadGenericService(ctx)
	goctx := context.WithValue(ctx.Context, dubboconst.AttachmentKey, ctx.attachments)

	if genericService, ok := proxy.(*generic.GenericService); ok {
		args, ok := ctx.values.([]hessian.Object)
		if !ok {
			return nil, &component.ServerError{
				StatusCode: 500,
				Message:    "dubbo参数解析失败",
				ErrorCode:  "DUBBO_PARAM_PARSE_ERROR",
			}
		}

		ret, e := genericService.Invoke(goctx, ctx.method, ctx.types, args)
		if e != nil {
			trace.Errorw("Dubbo invoke error", "error", e)
			return nil, &component.ServerError{
				StatusCode: 500,
				Message:    e.Error(),
				ErrorCode:  "DUBBO_INVOKE_ERROR",
			}
		}
		return ret, nil
	}
	return nil, &component.ServerError{
		StatusCode: 500,
		Message:    "dubbo服务不是泛化服务",
		ErrorCode:  "DUBBO_SERVICE_NOT_GENERIC",
	}
}

// loadGenericService 加载泛化服务
func (d *DubboInvoker) loadGenericService(ctx *DubboContext) common.RPCService {
	proxyId := ctx.group + "/" + ctx.interfaceName + ":" + ctx.version
	d.lock.Lock()
	defer d.lock.Unlock()
	if proxy := dubboc.GetConsumerService(proxyId); proxy != nil {
		return proxy
	}
	ref := dubboc.NewReferenceConfigBuilder().
		SetInterface(ctx.interfaceName).
		//SetRegistryIDs(b.registerIds...).
		SetProtocol(ctx.Protocol).
		SetGeneric(ctx.Generic).
		SetGroup(ctx.group).
		SetRequestTimeout(ctx.Timeout).
		SetRetries(ctx.Retries).
		SetVersion(ctx.version).
		SetCluster(ctx.Cluster).
		SetLoadbalance(ctx.LoadBalance).
		SetSerialization(ctx.Serialization).
		Build()

	rootConfig := dubboc.GetRootConfig()
	if err := ref.Init(rootConfig); err != nil {
		return nil
	}

	genericService := generic.NewGenericService(proxyId)
	ref.Refer(genericService)
	ref.Implement(genericService)
	dubboc.SetConsumerService(genericService)
	return genericService
}
